package com.axonactive.yolo.service.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import com.axonactive.yolo.dto.AirlinesFilter;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.dto.Price;
import com.axonactive.yolo.monitor.ServiceProvider;
import com.axonactive.yolo.service.interfaces.ExchangeRateService;
import com.axonactive.yolo.util.CONSTANTS;
import com.axonactive.yolo.util.Utils;

/**
 * Provide the methods to operate with Flights
 * 
 * @author pqtrung
 * 
 */

public class FlightFactoryService {
	private ExchangeRateService exchangeRateService = (ExchangeRateService) ServiceProvider.getInstance().getService(ExchangeRateService.class);

	private List<List<Flight>> sources = null;
	private Parameters param;
	private Logger logger = Logger.getLogger(FlightFactoryService.class);

	public FlightFactoryService() {
		sources = new ArrayList<List<Flight>>();
	}

	public List<Flight> getFlightsAfterComparedFromSources() {
		if (this.sources.size() == 0)
			return new ArrayList<Flight>();
		List<Flight> flights = new ArrayList<Flight>();
		flights = this.sources.get(0);
		for (int i = 1; i < this.sources.size(); i++) {
			List<Flight> tempList = this.sources.get(i);
			int newSizeOfFlights = flights.size();
			for (int j = 0; j < tempList.size(); j++) {
				int k = hasTheSameSignature(flights, tempList.get(j).getSignature(), newSizeOfFlights);
				if (k > -1) {
					if (flights.get(k).getAmountPrice() > tempList.get(j).getAmountPrice()) {
						flights.set(k, tempList.get(j));
					}
				} else {
					flights.add(tempList.get(j));
				}
			}
		}
		return flights;
	}

	public List<Flight> getFlightsAfterComparedFromSourcesAndFilterByStop(String numStops) {
		if (this.sources.size() == 0)
			return new ArrayList<Flight>();
		List<Flight> flights = new ArrayList<Flight>();
		flights = removeFlightsOverTheNumStops(this.sources.get(0), numStops);
		for (int i = 1; i < this.sources.size(); i++) {
			List<Flight> tempList = this.sources.get(i);
			int newSizeOfFlights = flights.size();
			for (int j = 0; j < tempList.size(); j++) {
				if (isOverNumStops(tempList.get(j), numStops) == true)
					continue;
				int k = hasTheSameSignature(flights, tempList.get(j).getSignature(), newSizeOfFlights);
				if (k > -1) {
					if (flights.get(k).getAmountPrice() > tempList.get(j).getAmountPrice()) {
						flights.set(k, tempList.get(j));
					}
				} else {
					flights.add(tempList.get(j));
				}
			}
		}
		return flights;
	}

	public List<Flight> getFlightAfterFilterByAirlines(List<Flight> flights, String airline, String airlinesFilterMode) {
		logger.info("Airlines: " + airline);
		List<String> airlineCode = new ArrayList<String>();
		List<String> airlineName = getAirlines(airline, airlineCode);
		for (Iterator<String> iterator = airlineName.iterator(); iterator.hasNext();) {
			String string = (String) iterator.next();
			logger.info("item:" + string);
		}

		List<Flight> rs = new ArrayList<Flight>();
		for (Iterator<Flight> iterator = flights.iterator(); iterator.hasNext();) {
			Flight flight = (Flight) iterator.next();
			if ("like".equals(airlinesFilterMode)) {
				if (isBelongToAirlines(flight, airlineCode, airlineName))
					rs.add(flight);
			} else {
				if (isNotBelongToAirlines(flight, airlineCode, airlineName))
					rs.add(flight);
			}
		}
		return rs;
	}

	private List<String> getAirlines(String airline, List<String> airlineCode) {
		List<String> airlineName = new ArrayList<String>();
		String[] temps = airline.split(CONSTANTS.TOKEN_DELIMITER);
		for (int i = 0; i < temps.length; i++) {
			String[] airlineStr = temps[i].split("-");
			airlineCode.add(airlineStr[0].trim());
			airlineName.add(airlineStr[1].trim().toLowerCase());
		}

		return airlineName;
	}

	/**
	 * This method scan and compare all airport, not only first airport.
	 * 
	 * @param flight
	 * @param airlineCode
	 * @param airlineName
	 * @return
	 */
	private boolean isBelongToAirlines(Flight flight, List<String> airlineCode, List<String> airlineName) {
		if (flight.isRoundTrip() == true) {
			if (airlineCode.contains(flight.getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2)) && (airlineCode.contains(flight.getInBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2)))) {
				return true;
			}
		} else {
			if (airlineCode.contains(flight.getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2))) {
				return true;
			}
		}
		return false;
	}

	private boolean isNotBelongToAirlines(Flight flight, List<String> airlineCode, List<String> airlineName) {
		if (flight.isRoundTrip() == true) {
			if (airlineCode.contains(flight.getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2)) || (airlineCode.contains(flight.getInBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2)))) {
				return false;
			}
		} else {
			if (airlineCode.contains(flight.getOutBound().getDepartAirports().get(0).getAirline().getFlightNumber().substring(0, 2))) {
				return false;
			}
		}
		return true;
	}

	private boolean isOverNumStops(Flight flight, String numStops) {
		int stops = Utils.convertToInt(numStops);
		int numStopsInbound = 0;
		if (flight.isRoundTrip())
			numStopsInbound = Utils.convertToInt(flight.getInBound().getNumberStops());
		int numStopsOutbound = Utils.convertToInt(flight.getOutBound().getNumberStops());
		if (numStops.equals(CONSTANTS.FILTER_NO_STOP) || numStops.equals(CONSTANTS.FILTER_ONE_STOP)) {
			if (numStopsInbound > stops || numStopsOutbound > stops)
				return true;
		}
		// default is FILTER_OVER_ONE_STOP, it means that list all results
		return false;
	}

	private List<Flight> removeFlightsOverTheNumStops(List<Flight> flights, String numStops) {
		List<Flight> rs = new ArrayList<Flight>();
		for (Iterator<Flight> iterator = flights.iterator(); iterator.hasNext();) {
			Flight flight = (Flight) iterator.next();
			if (isOverNumStops(flight, numStops))
				continue;
			rs.add(flight);
		}
		return rs;
	}

	private int hasTheSameSignature(List<Flight> flights, String signature, int endIndex) {
		for (int i = 0; i < endIndex; i++) {
			if (flights.get(i).getSignature().equals(signature))
				return i;
		}
		return -1;
	}

	/**
	 * Add flights to repository to process later (compare or filter,etc)
	 * 
	 * @param flights
	 */
	public void addToRepository(List<Flight> flights) {
		if (flights.size() > 0)
			sources.add(flights);
	}

	public List<List<Flight>> getRepository() {
		return sources;
	}

	public void clearRepository() {
		this.sources = new ArrayList<List<Flight>>();
	}

	/**
	 * In the flights have the same signature, only chose the best flight from
	 * the flights which is gotten from one API
	 * 
	 * @param flights
	 * @return list of flight after remove the doubling flights
	 */
	public List<Flight> removeTheDoublingFlights(List<Flight> flights) {
		List<String> signatureList = new ArrayList<String>();
		int size = flights.size();
		for (int i = 0; i < size; i++) {
			String signature = flights.get(i).getSignature();
			int k = signatureList.indexOf(signature);
			if (k > -1) {
				if (flights.get(k).getAmountPrice() > flights.get(i).getAmountPrice()) {
					flights.set(k, flights.get(i));
				}
				flights.remove(i);
				i--;
				size--;
				continue;
			}
			signatureList.add(signature);
		}
		return flights;
	}

	/**
	 * Remove the flights have the incorrect departure day
	 * 
	 * @param flights
	 * @return
	 */
	public List<Flight> removeTheWrongFlights(List<Flight> flights) {
		int size = flights.size();
		String outBoundDateParameter = param.getOutBound();
		// logger.info("outBoundDateParameter: " + outBoundDateParameter);
		String inBoundDaterameter = param.getInBound();
		// logger.info("inBoundDateParameter: " + inBoundDaterameter + " size="
		// + inBoundDaterameter.length());
		if (param.getTypeOfTrip().equals(CONSTANTS.ROUND_TRIP)) {
			for (int i = 0; i < size; i++) {
				String outBoundDate = flights.get(i).getOutBound().getDepartAirports().get(0).getDepartureTimeYYYYMMDD();
				String inBoundDate = flights.get(i).getInBound().getDepartAirports().get(0).getDepartureTimeYYYYMMDD();
				if (!outBoundDate.equals(outBoundDateParameter) || !inBoundDate.equals(inBoundDaterameter)) {
					flights.remove(i);
					i--;
					size--;
					continue;
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				String outBoundDate = flights.get(i).getOutBound().getDepartAirports().get(0).getDepartureTimeYYYYMMDD();
				if (!outBoundDate.equals(outBoundDateParameter)) {
					flights.remove(i);
					i--;
					size--;
					continue;
				}
			}

		}
		return flights;
	}

	/**
	 * convert the amount from RUB (API) to expected input currency then filter
	 * by maxPrice
	 * 
	 * @param flights
	 *            using exchangeRateService to convert
	 * @return
	 */
	public List<Flight> convertAndFilterAmountByInputCurrency(String currencyCode, double maxPrice) {
		if (this.sources.size() == 0)
			return new ArrayList<Flight>();
		List<Flight> flights = new ArrayList<Flight>();
		flights = this.sources.get(0);
		double oldRate = exchangeRateService.getRateByOldCode("RUB");
		double newRate = exchangeRateService.getRateByNewCode(currencyCode);
		for (int i = 0; i < flights.size(); i++) {
			Price price = new Price();
			price.setCurrencyCode(currencyCode);
			double newAmount = Double.parseDouble(flights.get(i).getPrice().getAmount());
			newAmount = newAmount * (newRate / oldRate);
			String newAmountFormat = String.format("%.2f", newAmount).replace(',', '.');
			price.setAmount(newAmountFormat);
			flights.get(i).setPrice(price);
			if (maxPrice != CONSTANTS.DEFAULT_MAX_PRICE) {
				if (newAmount > maxPrice) {
					flights.remove(i);
					i--;
				}
			}
		}
		return flights;
	}

	public void setParameter(Parameters param) {
		this.param = param;
	}

	/**
	 * get all airlines name and amount
	 * 
	 * @param flight
	 *            - Object Flight
	 * @param airlinesFilter
	 *            - List of AirlinesFilter
	 */
	public void getAllAirlinesAndAmount(Flight flight, List<AirlinesFilter> airlinesFilter) {
		AirlinesFilter airline = new AirlinesFilter();
		String airlineName = flight.getOutBound().getDepartAirports().get(0).getAirline().getName().trim();
		airline.setName(airlineName);
		boolean check = false;
		int i;
		for (i = 0; i < airlinesFilter.size(); i++) {
			if (airlinesFilter.get(i).getName().equals(airline.getName())) {
				// carrier already exist in list, just increase amount of
				// carrier

				airlinesFilter.get(i).setAmount(airlinesFilter.get(i).getAmount() + 1);
				check = true;
				// LOGGER.info("airlinesFilter.get(i).getName() " +
				// airlinesFilter.get(i).getName());
				break;
			}
		}
		if (check == false) {
			// carrier doesn't exist in list, push it into list
			airlinesFilter.add(airline);
		}
		if (flight.isRoundTrip()) {
			// in case of round trip, compare carrier of inbound and carrier of
			// outbound,
			// if it equal, we ignore, otherwise, continue loop to check if it
			// already exist in list or not
			String inBoundAirlineName = flight.getInBound().getDepartAirports().get(0).getAirline().getName().trim();
			for (i = 0; i < airlinesFilter.size(); i++) {
				if (airlinesFilter.get(i).getName().equals(inBoundAirlineName)) {
					// carrier already exist in list, just increase amount of
					// carrier
					airlinesFilter.get(i).setAmount(airlinesFilter.get(i).getAmount() + 1);
					return;
				}
			}
			AirlinesFilter carrierIB = new AirlinesFilter();
			carrierIB.setAmount(1);
			carrierIB.setName(inBoundAirlineName);
			airlinesFilter.add(carrierIB);
		}
	}

	/**
	 * sort airlines by name
	 * 
	 * @param airlines
	 *            - List of AirlinesFilter
	 */
	public void sortAirlinesByName(List<AirlinesFilter> airlines) {
		AirlinesFilter airlineTmp;
		for (int i = 0; i < airlines.size() - 1; i++) {
			for (int j = i; j < airlines.size(); j++) {
				if (airlines.get(i).getName().compareTo(airlines.get(j).getName()) > 0) {
					airlineTmp = airlines.get(i);
					airlines.set(i, airlines.get(j));
					airlines.set(j, airlineTmp);

				}
			}
		}
	}
}
